// -----------------------------------------------------------------------------
// chargen_h
// -----------------------------------------------------------------------------
/*
    Character Generation Header
*/
// -----------------------------------------------------------------------------
// owner: georg zoeller
// -----------------------------------------------------------------------------

#include "plt_gen00pt_backgrounds"
#include "wrappers_h"
#include "core_h"
#include "sys_chargen_engine"
#include "ai_constants_h"


const int ABILITY_TALENT_TRAIT_HUMANOID = 4002;
const float CHARGEN_BASE_ATTRIBUTE_VALUE = 10.0f;


// -----------------------------------------------------------------------------
/*  @brief Add ability wrapper  */
void _AddAbility(object oChar, int nAbility, int bRemove = FALSE)
{
    if (nAbility != 0)
    {
        if (!bRemove)
        {
            AddAbility(oChar,nAbility);
            SetQuickslot(oChar, -1, nAbility);
            //Kurapica Add For AllCanLearn Start
            if (nAbility > 9000000)
            {
                AddAbility(oChar,nAbility-9000000);
            }
            //Kurapica Add For AllCanLearn End
        }
        else
        {
            RemoveAbility(oChar, (-1) * nAbility);
            //Kurapica Add For AllCanLearn Start
            if (nAbility < -9000000)
            {
                RemoveAbility(oChar,(-1) * (nAbility+9000000));
            }
            //Kurapica Add For AllCanLearn End
        }
    }
}


// -----------------------------------------------------------------------------
/*
 *  @brief Modifiers a creature property base value by a given number
 *
 *  @param oChar        The character
 *  @param nProperty    The property to modifiy
 *  @param fModifyBy    The amount to modify by
 *
 *  @author Georg Zoeller
*/
void Chargen_ModifyCreaturePropertyBase(object oChar, int nProperty, float fModifyBy);
void Chargen_ModifyCreaturePropertyBase(object oChar, int nProperty, float fModifyBy)
{
     if (fModifyBy != 0.0f)
     {
        float fCur = GetCreatureProperty(oChar,nProperty, PROPERTY_VALUE_BASE);

        #ifdef DEBUG
        if (IsPartyMember(oChar))
        {
            Log_Trace(LOG_CHANNEL_CHARACTER,"sys_chargen_tools_h.ModPropBase","Modifying prop " + ToString(nProperty) + " by " + ToString(fModifyBy) + " = " + ToString(fCur + fModifyBy) );
        }
        #endif


        fCur += fModifyBy;
        SetCreatureProperty(oChar, nProperty, fCur,PROPERTY_VALUE_BASE);

        if (IsUsingEP1Resources() == TRUE)
        {
            // modify explore regeneration rates if applicable
            if (nProperty == PROPERTY_ATTRIBUTE_CONSTITUTION) // health regeneration
            {
                float fRegenCur = GetCreatureProperty(oChar, PROPERTY_ATTRIBUTE_REGENERATION_HEALTH, PROPERTY_VALUE_BASE);
                float fRegen = fModifyBy * 0.5f;
                #ifdef DEBUG
                Log_Trace(LOG_CHANNEL_CHARACTER, "sys_chargen_tools_h.ModPropBase", "Modifying Health Regeneration rate from " + ToString(fRegenCur) + " by " + ToString(fRegen) + " to " + ToString(fRegenCur + fRegen));
                #endif
                fRegenCur += fRegen;
                SetCreatureProperty(oChar, PROPERTY_ATTRIBUTE_REGENERATION_HEALTH, fRegenCur, PROPERTY_VALUE_BASE);
            } else if (nProperty == PROPERTY_ATTRIBUTE_WILLPOWER) // mana/stamina regeneration
            {
                float fRegenCur = GetCreatureProperty(oChar, PROPERTY_ATTRIBUTE_REGENERATION_STAMINA, PROPERTY_VALUE_BASE);
                float fRegen = fModifyBy * 0.875f;
                #ifdef DEBUG
                Log_Trace(LOG_CHANNEL_CHARACTER, "sys_chargen_tools_h.ModPropBase", "Modifying Stamina/Mana Regeneration rate from " + ToString(fRegenCur) + " by " + ToString(fRegen) + " to " + ToString(fRegenCur + fRegen));
                #endif
                fRegenCur += fRegen;
                SetCreatureProperty(oChar, PROPERTY_ATTRIBUTE_REGENERATION_STAMINA, fRegenCur, PROPERTY_VALUE_BASE);
            }
        }

        // ---------------------------------------------------------------------
        // If the property is of a depletable type, synch the total to the max.
        // This causes the 'heal on levelup' effect
        // ---------------------------------------------------------------------
        if (GetCreaturePropertyType(oChar,nProperty) == PROPERTY_TYPE_DEPLETABLE)
        {
            fCur = GetCreatureProperty(oChar,nProperty, PROPERTY_VALUE_TOTAL);

            if (!IsDead(oChar))
                SetCreatureProperty(oChar,nProperty,fCur,PROPERTY_VALUE_CURRENT);
        }
     }
     else
     {
       #ifdef DEBUG
       Log_Trace(LOG_CHANNEL_CHARACTER,"sys_chargen_tools_h.ModPropBase","Not modifying prop due to zero value " + ToString(nProperty));
       #endif
     }

}

// -----------------------------------------------------------------------------
/*
 *  @brief Applies the starting attribute modifiers from selecting a race
 *
 *  Reads the attribute modifiers for a race from the 2da and applies it
 *
 *  @param oChar    The character
 *  @param nRace    The race (index into race_base.xls)
 *  @param bUnApply Whether to apply (default) or unapply the modifier
 *
 *  @author Georg Zoeller
*/
void Chargen_ApplyRaceAttributeModifiers(object oChar, int nRace, int bUnApply = FALSE);
void Chargen_ApplyRaceAttributeModifiers(object oChar, int nRace, int bUnApply = FALSE)
{

    #ifdef DEBUG
    Log_Trace(LOG_CHANNEL_CHARACTER,
        "sys_charge_h.ApplyRaceAttributeModifiers","Setting Racial Modifiers for Race " + ToString(nRace) + "bUnApply:" + ToString(bUnApply));
    #endif

    int nUnApply = (bUnApply)?-1:1;

    // -------------------------------------------------------------------------
    // 1. Read the modifier values in
    //    - Clean this 2da mess up, there's a better structure than this.
    // -------------------------------------------------------------------------
    float fStrMod  = GetM2DAFloat(TABLE_RULES_RACES,"StrAdjust",nRace) * nUnApply;
    float fDexMod  = GetM2DAFloat(TABLE_RULES_RACES,"DexAdjust",nRace) * nUnApply;
    float fIntMod  = GetM2DAFloat(TABLE_RULES_RACES,"IntAdjust",nRace) * nUnApply;
    float fWillMod = GetM2DAFloat(TABLE_RULES_RACES,"WillAdjust",nRace)* nUnApply;
    float fConMod  = GetM2DAFloat(TABLE_RULES_RACES,"ConAdjust",nRace) * nUnApply;
    float fMagMod  = GetM2DAFloat(TABLE_RULES_RACES,"MagAdjust",nRace) * nUnApply;


    // -------------------------------------------------------------------------
    // 2. Modify the creature properties based on the values
    // -------------------------------------------------------------------------
    Chargen_ModifyCreaturePropertyBase(oChar, PROPERTY_ATTRIBUTE_STRENGTH,       fStrMod);
    Chargen_ModifyCreaturePropertyBase(oChar, PROPERTY_ATTRIBUTE_DEXTERITY,      fDexMod);
    Chargen_ModifyCreaturePropertyBase(oChar, PROPERTY_ATTRIBUTE_INTELLIGENCE,   fIntMod);
    Chargen_ModifyCreaturePropertyBase(oChar, PROPERTY_ATTRIBUTE_WILLPOWER,      fWillMod);
    Chargen_ModifyCreaturePropertyBase(oChar, PROPERTY_ATTRIBUTE_CONSTITUTION,   fConMod);
    Chargen_ModifyCreaturePropertyBase(oChar, PROPERTY_ATTRIBUTE_MAGIC,          fMagMod);

}




// -----------------------------------------------------------------------------
/*
 *  @brief Applies the starting attribute modifiers from selecting a class
 *
 *  Reads the attribute modifiers for a class from the 2da and applies it
 *
 *  @param oChar    The character
 *  @param nRace    The class (index into cla_base.xls)
 *  @param bUnApply Whether to apply (default) or unapply the modifier
 *
 *  @author Georg Zoeller
*/
void Chargen_ApplyClassAttributeModifiers(object oChar, int nClass, int bUnApply = FALSE);
void Chargen_ApplyClassAttributeModifiers(object oChar, int nClass, int bUnApply = FALSE)
{

    int nModifier = (bUnApply)?-1:1;

    // -------------------------------------------------------------------------
    // 1. Read the modifier values in
    //    - Clean this 2da mess up, there's a better structure than this.
    // -------------------------------------------------------------------------
    float fStrMod  = GetM2DAFloat(TABLE_RULES_CLASSES,"StrAdjust",nClass) * nModifier;
    float fDexMod  = GetM2DAFloat(TABLE_RULES_CLASSES,"DexAdjust",nClass) * nModifier;
    float fIntMod  = GetM2DAFloat(TABLE_RULES_CLASSES,"IntAdjust",nClass) * nModifier;
    float fWillMod = GetM2DAFloat(TABLE_RULES_CLASSES,"WillAdjust",nClass)* nModifier;
    float fConMod  = GetM2DAFloat(TABLE_RULES_CLASSES,"ConAdjust",nClass) * nModifier;
    float fMagMod  = GetM2DAFloat(TABLE_RULES_CLASSES,"MagAdjust",nClass) * nModifier;


    // -------------------------------------------------------------------------
    // 2. Modify the creature properties based on the values
    // -------------------------------------------------------------------------

    #ifdef DEBUG
    if (LOG_ENABLED)
    {
         Log_Trace(LOG_CHANNEL_TEMP,"sys_chargen_h", "STR +" + ToString(fStrMod));
        Log_Trace(LOG_CHANNEL_TEMP,"sys_chargen_h", "DEX +" + ToString(fDexMod));
        Log_Trace(LOG_CHANNEL_TEMP,"sys_chargen_h", "CON +" + ToString(fConMod));
        Log_Trace(LOG_CHANNEL_TEMP,"sys_chargen_h", "WIL +" + ToString(fWillMod));
        Log_Trace(LOG_CHANNEL_TEMP,"sys_chargen_h", "MAG +" + ToString(fMagMod));
        Log_Trace(LOG_CHANNEL_TEMP,"sys_chargen_h", "INT+" + ToString(fIntMod));
    }
    #endif


    Chargen_ModifyCreaturePropertyBase(oChar, PROPERTY_ATTRIBUTE_STRENGTH,      fStrMod);
    Chargen_ModifyCreaturePropertyBase(oChar, PROPERTY_ATTRIBUTE_DEXTERITY,     fDexMod);
    Chargen_ModifyCreaturePropertyBase(oChar, PROPERTY_ATTRIBUTE_INTELLIGENCE,  fIntMod);
    Chargen_ModifyCreaturePropertyBase(oChar, PROPERTY_ATTRIBUTE_WILLPOWER,     fWillMod);
    Chargen_ModifyCreaturePropertyBase(oChar, PROPERTY_ATTRIBUTE_CONSTITUTION,  fConMod);
    Chargen_ModifyCreaturePropertyBase(oChar, PROPERTY_ATTRIBUTE_MAGIC,         fMagMod);



}


// -----------------------------------------------------------------------------
/*
 *  @brief Applies the starting stat modifiers from selecting a class
 *
 *  Reads the statmodifiers for a class from the 2da and applies it
 *
 *  @param oChar    The character
 *  @param nRace    The class (index into cla_base.xls)
 *  @param bUnApply Whether to apply (default) or unapply the modifier
 *
 *  @author Georg Zoeller
*/
void Chargen_ApplyClassStatModifiers(object oChar, int nClass, int bUnApply = FALSE, float fRankModifier = 1.0f);
void Chargen_ApplyClassStatModifiers(object oChar, int nClass, int bUnApply = FALSE, float fRankModifier = 1.0f)
{

    int nModifier = (bUnApply)?-1:1;

    Log_Chargen("Chargen_ApplyClassStatModifiers","nModifier: " + ToString(nModifier) + " nRankModifier" + ToString(fRankModifier) + " Player Rank:" + ToString(GetCreatureRank(oChar)));

    // -------------------------------------------------------------------------
    // 1. Read the modifier values in
    //    - Clean this 2da mess up, there's a better structure than this.
    // -------------------------------------------------------------------------

     float fBaseAttack = GetM2DAFloat(TABLE_RULES_CLASSES,"BaseAttack",nClass) * nModifier;
     float fBaseDefense = GetM2DAFloat(TABLE_RULES_CLASSES,"BaseDefense",nClass) * nModifier;

    Chargen_ModifyCreaturePropertyBase(oChar, PROPERTY_ATTRIBUTE_ATTACK,        fBaseAttack);
    Chargen_ModifyCreaturePropertyBase(oChar, PROPERTY_ATTRIBUTE_DEFENSE,       fBaseDefense);


    float fDepletableMod = GetClassDataFloat(CLASS_DATA_BASE_MANA_STAMINA, nClass)  * fRankModifier  *     nModifier;

    float fRankModifierHealth = GetAutoScaleDataFloat(GetCreatureRank(oChar),AS_RANK_HEALTH_SCALE_FACTOR);

    //bugfix:
    if (fRankModifierHealth == 0.0f)
    {
        #ifdef DEBUG
        Warning("Creature with no rank (0) trying to apply class, causing 0 health. Please contact georg. Details: " + ToString(oChar) + " " + GetCurrentScriptName());
        #endif
        fRankModifierHealth = 1.0f;
    }

   // we subtract 1 as the stat is initialized at 1 to prevent the character from dying

    float fHealthMod = (((GetClassDataFloat(CLASS_DATA_BASE_HEALTH,nClass))    * fRankModifierHealth)  *     nModifier) - 1.0f;
    float fDamageMod = GetClassDataFloat(CLASS_DATA_DAMAGE_BONUS, nClass);

    // -------------------------------------------------------------------------
    // 2. Modify the creature properties based on the values
    // -------------------------------------------------------------------------
    Chargen_ModifyCreaturePropertyBase(oChar, PROPERTY_DEPLETABLE_MANA_STAMINA,  fDepletableMod * nModifier);
    Chargen_ModifyCreaturePropertyBase(oChar, PROPERTY_DEPLETABLE_HEALTH,        fHealthMod * nModifier);
    Chargen_ModifyCreaturePropertyBase(oChar, PROPERTY_ATTRIBUTE_DAMAGE_BONUS ,        fDamageMod * nModifier);

}

// -----------------------------------------------------------------------------
/*
 *  @brief Applies the abilities granted to a character because of picking a class
 *
 *  Reads the class from cla_base.xls and applies it
 *
 *  @param oChar    The character
 *  @param nClass
 *  @param bUnApply Whether to apply (default) or unapply the modifier
 *
 *  @author Georg Zoeller
*/

void Chargen_ApplyClassAbilities(object oChar, int nClass, int bUnApply = FALSE);
void Chargen_ApplyClassAbilities(object oChar, int nClass, int bUnApply = FALSE)
{
    // -------------------------------------------------------------------------
    // 1. Ability #1
    // -------------------------------------------------------------------------
    int nAbility = GetM2DAInt(TABLE_RULES_CLASSES,"StartingAbility1",nClass);
    _AddAbility(oChar, nAbility, bUnApply);


    // -------------------------------------------------------------------------
    // 2. Ability #2
    // -------------------------------------------------------------------------
    nAbility = GetM2DAInt(TABLE_RULES_CLASSES,"StartingAbility2",nClass);
    _AddAbility(oChar, nAbility, bUnApply);


    // -------------------------------------------------------------------------
    // Starting Skills
    // -------------------------------------------------------------------------
    if (nClass == CLASS_WARRIOR)
    {
        _AddAbility(oChar, 100100);
    }
    else if (nClass == CLASS_ROGUE)
    {
        _AddAbility(oChar, ABILITY_SKILL_POISON_1);
    }
    else if (nClass == CLASS_WIZARD)
    {
        _AddAbility(oChar, ABILITY_SKILL_HERBALISM_1);
    }
}



int ChargenGetBackgroundSkill (int nRace, int nBackground)
{
 // the skill to add is found in the column postfixed with the race id of the character.
    string sCol  = "Skill_" + ToString(nRace);
    int nSkill = GetM2DAInt(TABLE_RULES_BACKGROUNDS, sCol, nBackground);
    return nSkill;

}

/*
 *  @brief
 *
 *      Selects the creature's gender.
 *
 *  @param oChar The character
 *
 *  @author Georg Zoeller
*/
void Chargen_SelectGender(object oChar,int nGender);
void Chargen_SelectGender(object oChar,int nGender)
{
    // -------------------------------------------------------------------------
    // 1. Set the Gender
    //    - Write engine function to set gender
    //    - Obviously we don't want this to happen to existing creatures...
    // -------------------------------------------------------------------------
    SetCreatureGender(oChar, nGender);
}



/*
 *  @brief Generate a unique ID for each class_race_background combination
 *         to allow one step lookup into the 2da.
 *
 *  @author Georg Zoeller
*/
int Chargen_GetEquipIndex(int nRace, int nClass, int nBackground)
{
    return nRace * 1000 + nClass * 100+ nBackground;
}


void Chargen_GetItemForSlot(object oCreature, int nSlot, int nWeaponSet = 1, int nClass = 0, int nIdx = 0)
{
    /*
    int INVENTORY_SLOT_MAIN                  =  0;
    int INVENTORY_SLOT_OFFHAND               =  1;
    int INVENTORY_SLOT_RANGEDAMMO            =  2;
    int INVENTORY_SLOT_CHEST                 =  4;
    int INVENTORY_SLOT_HEAD                  =  5;
    int INVENTORY_SLOT_BOOTS                 =  6;
    int INVENTORY_SLOT_GLOVES                =  7;
    */


    resource r;
    if (nIdx != 0)
    {
        r = GetM2DAResource(TABLE_STARTING_EQUIPMENT,"Slot"+IntToString(nSlot), nIdx);
    }

    if (r != INVALID_RESOURCE)
    {
        object oExists = GetItemInEquipSlot(nSlot, oCreature, nWeaponSet);
        DestroyObject(oExists);
        object oItem  = CreateItemOnObject(r,oCreature, 1,"",TRUE,FALSE);
        if (IsObjectValid(oItem))
        {
            EquipItem(oCreature, oItem, nSlot, nWeaponSet);
        }
    }


}

void Chargen_ClearInventory(object oCreature)
{
    object[] items = GetItemsInInventory(oCreature,GET_ITEMS_OPTION_EQUIPPED);
    int nSize = GetArraySize(items);
    int i;

    for (i = 0; i< nSize; i++)
    {
        DestroyObject(items[i],0);
    }
}


void Chargen_InitInventory (object oCreature, int nClass = 0, int nIdx = 0)
{

    #ifdef DEBUG
    Log_Chargen("Chargen_InitInventory","-- Initializing Inventory",oCreature);
    #endif

    if (IsPartyMember(oCreature))
    {
        Chargen_ClearInventory(oCreature);
    }

    // ---------------------------------------------------------------------
    // If we have an index, then a proper class/race/background combination was
    // passed.
    // ---------------------------------------------------------------------
    if (nIdx != 0)
    {

        // ---------------------------------------------------------------------
        // Load the starting equipment of the memory cached resources defined
        // in the 2da
        // ---------------------------------------------------------------------
        string sTemplate = GetM2DAString(TABLE_STARTING_EQUIPMENT,"Template", nIdx);
        LoadItemsFromTemplate(oCreature, sTemplate, TRUE);

        // ---------------------------------------------------------------------
        // Load the starting ability for the background
        // ---------------------------------------------------------------------
        int nAbility = GetM2DAInt(TABLE_STARTING_EQUIPMENT,"Ability", nIdx);
        _AddAbility(oCreature, nAbility);

    }
    else
    {
        string sTemplate = "default_player.utc";
        switch (nClass)
        {
            case CLASS_WARRIOR: sTemplate = "default_warrior.utc";
                break;
            case CLASS_ROGUE:   sTemplate = "default_rogue.utc";
                break;
            case CLASS_WIZARD:  sTemplate = "default_wizard.utc";
                break;
        }

        LoadItemsFromTemplate(oCreature, sTemplate, TRUE);
    }
}


/*
 *  @brief Initializes a blank character at level 0
 *
 *  Sets up a blank character with level 0, initializes the default attribute
 *  values and removes any preexisting abilities, effects, etc.
 *
 *  @param oChar The character
 *
 *  @author Georg Zoeller
*/
void Chargen_InitializeCharacter(object oChar, int bWipeAbilities = TRUE)
{

    #ifdef DEBUG
    Log_Chargen("Chargen_InitializeCharacter"," --------- INITIALIZE CHARACTER --------- ",oChar);
    #endif

    // -------------------------------------------------------------------------
    // 1.  Clear all creature properties, resulting in an absolute empty creature
    //     - Engine_ClearInventory(oChar);
    //     - Engine_ClearAllAbilities(oChar);
    //     - Engine_ClearAllEffects(oChar);
    // -------------------------------------------------------------------------


    // -------------------------------------------------------------------------
    // 2. Set Creature to Level and Experience 0
    // -------------------------------------------------------------------------
    SetCreatureProperty(oChar,PROPERTY_SIMPLE_LEVEL,1.0f,PROPERTY_VALUE_BASE);
    SetCreatureProperty(oChar,PROPERTY_SIMPLE_EXPERIENCE,0.0f,PROPERTY_VALUE_BASE);


    // -------------------------------------------------------------------------
    // 3. Initialize all creature attributes to default value
    // -------------------------------------------------------------------------
    SetCreatureProperty(oChar,PROPERTY_ATTRIBUTE_STRENGTH,      CHARGEN_BASE_ATTRIBUTE_VALUE, PROPERTY_VALUE_BASE);
    SetCreatureProperty(oChar,PROPERTY_ATTRIBUTE_DEXTERITY,     CHARGEN_BASE_ATTRIBUTE_VALUE, PROPERTY_VALUE_BASE);
    SetCreatureProperty(oChar,PROPERTY_ATTRIBUTE_CONSTITUTION,  CHARGEN_BASE_ATTRIBUTE_VALUE, PROPERTY_VALUE_BASE);
    SetCreatureProperty(oChar,PROPERTY_ATTRIBUTE_WILLPOWER,     CHARGEN_BASE_ATTRIBUTE_VALUE, PROPERTY_VALUE_BASE);
    SetCreatureProperty(oChar,PROPERTY_ATTRIBUTE_INTELLIGENCE,  CHARGEN_BASE_ATTRIBUTE_VALUE, PROPERTY_VALUE_BASE);
    SetCreatureProperty(oChar,PROPERTY_ATTRIBUTE_MAGIC,         CHARGEN_BASE_ATTRIBUTE_VALUE, PROPERTY_VALUE_BASE);

    SetCreatureProperty(oChar,PROPERTY_ATTRIBUTE_ATTACK_SPEED_MODIFIER,     1.0f);
    SetCreatureProperty(oChar,PROPERTY_ATTRIBUTE_DAMAGE_SCALE,       1.0f);
    SetCreatureProperty(oChar,PROPERTY_ATTRIBUTE_RESISTANCE_MENTAL,         0.0f);
    SetCreatureProperty(oChar,PROPERTY_ATTRIBUTE_RESISTANCE_PHYSICAL,       0.0f);

    SetCreatureProperty(oChar,51,       1.0f);


    SetCreatureProperty(oChar, PROPERTY_ATTRIBUTE_REGENERATION_HEALTH_COMBAT,  REGENERATION_HEALTH_COMBAT_DEFAULT);
    SetCreatureProperty(oChar, PROPERTY_ATTRIBUTE_REGENERATION_STAMINA_COMBAT, REGENERATION_STAMINA_COMBAT_DEFAULT);

    SetCreatureProperty(oChar, PROPERTY_ATTRIBUTE_REGENERATION_HEALTH,REGENERATION_HEALTH_EXPLORE_DEFAULT, PROPERTY_VALUE_BASE);
    SetCreatureProperty(oChar, PROPERTY_ATTRIBUTE_REGENERATION_STAMINA, REGENERATION_STAMINA_EXPLORE_DEFAULT, PROPERTY_VALUE_BASE);
    SetCreatureProperty(oChar, PROPERTY_ATTRIBUTE_MISSILE_SHIELD,0.0);

    SetCreatureProperty(oChar, 38,0.0);

    // -------------------------------------------------------------------------
    // 4. Initialize all stats
    // -------------------------------------------------------------------------
    SetCreatureProperty(oChar, PROPERTY_DEPLETABLE_HEALTH ,          1.0f, PROPERTY_VALUE_BASE);
    SetCreatureProperty(oChar, PROPERTY_DEPLETABLE_MANA_STAMINA ,    0.0f, PROPERTY_VALUE_BASE);
    SetCreatureProperty(oChar, PROPERTY_ATTRIBUTE_DEFENSE ,          0.0f, PROPERTY_VALUE_BASE);
    SetCreatureProperty(oChar, PROPERTY_ATTRIBUTE_ATTACK,            0.0f, PROPERTY_VALUE_BASE);
    SetCreatureProperty(oChar, PROPERTY_ATTRIBUTE_DAMAGE_BONUS,      0.0f, PROPERTY_VALUE_BASE);
    SetCreatureProperty(oChar, PROPERTY_ATTRIBUTE_FLANKING_ANGLE,  60.0f, PROPERTY_VALUE_BASE);

    // -------------------------------------------------------------------------
    // 6. Set up points available to distribute (skills only on humanoids)
    // -------------------------------------------------------------------------
    if (IsHumanoid(oChar))
    {
        SetCreatureProperty(oChar,PROPERTY_SIMPLE_SKILL_POINTS,      1.0, PROPERTY_VALUE_BASE);
    }
    SetCreatureProperty(oChar,PROPERTY_SIMPLE_ATTRIBUTE_POINTS,  5.0, PROPERTY_VALUE_BASE);
    SetCreatureProperty(oChar,PROPERTY_SIMPLE_TALENT_POINTS,     2.0, PROPERTY_VALUE_BASE);



    // -------------------------------------------------------------------------
    // Undo Class and Race selection
    // -------------------------------------------------------------------------
    int nClass = GetCreatureCoreClass(oChar);
    if (bWipeAbilities)
    {

        #ifdef DEBUG
        Log_Chargen("Chargen_InitializeCharacter","* Clearing Ability List",oChar);
        #endif

        // We don't wipe the list if any hidden traits are present and
        // the target is not a party member to avoid losing presets made in the
        // toolset
        if (GetAbilityCount(oChar,ABILITY_TYPE_TALENT, 18) ==0 || IsPartyMember(oChar))
        {
            // Note: This wipes the ability list clear
            CharGen_ClearAbilityList(oChar,1);
            CharGen_ClearAbilityList(oChar,2);
            CharGen_ClearAbilityList(oChar,3);
        }
        else
        {
                #ifdef DEBUG
                Log_Chargen("Chargen_InitializeCharacter","* FAILED - Creature has talents of type 18"  ,oChar);
                #endif
        }
    }

    // -------------------------------------------------------------------------
    // add player hidden talent (used to show some special abilities)
    // -------------------------------------------------------------------------
   if (IsHero(oChar))
   {
        #ifdef DEBUG
        Log_Chargen("Chargen_InitializeCharacter","* Adding hidden player talent.",oChar);
        #endif
        AddAbility(oChar, 4001,FALSE);
   }

   if (IsPartyMember(oChar))
   {
        SetCreatureRank(oChar, 100 /*RANK_PLAYER*/);
   }


}

/*
 *  @brief applies the race modifiers based on a character's chosen race
 *
 *  @param oChar The character
 *
 *  @author Georg Zoeller
*/
void Chargen_ApplyRaceModifiers(object oChar, int bUndo = FALSE);
void Chargen_ApplyRaceModifiers(object oChar, int bUndo = FALSE)
{

    int nRace =GetCreatureRacialType(oChar);

    Log_Chargen("Chargen_ApplyRaceModifiers",(bUndo?"Un":"") +"Applying Race Modifiers",oChar);
    // -------------------------------------------------------------------------
    // 1. Setup the Racial Attribute Modifiers
    // -------------------------------------------------------------------------
    Chargen_ApplyRaceAttributeModifiers(oChar,nRace, bUndo);

    // -------------------------------------------------------------------------
    // 2. Ability #1
    // -------------------------------------------------------------------------
    int nAbility = GetM2DAInt(TABLE_RULES_RACES,"Ability1",nRace);
    if (bUndo)
    {
         RemoveAbility(oChar, nAbility);
    }
    else
    {
         _AddAbility(oChar, nAbility, FALSE);
    }



    // -------------------------------------------------------------------------
    // 3. Ability #2
    // -------------------------------------------------------------------------
    nAbility = GetM2DAInt(TABLE_RULES_RACES,"Ability2",nRace);
    if (bUndo)
    {
         RemoveAbility(oChar, nAbility);
    }
    else
    {
         _AddAbility(oChar, nAbility, FALSE);

       }
}


/*
 *  @brief Selects the character's race.
 *
 *      Selects the character's race including the corresponding attribute modifiers and abilities.
 *
 *  @param oChar The character
 *
 *  @author Georg Zoeller
*/
void Chargen_SelectRace(object oChar,int nRace, int bUndo = FALSE);
void Chargen_SelectRace(object oChar,int nRace, int bUndo = FALSE)
{

     Log_Chargen("Chargen_SelectRace","-- " + (bUndo?"Un":"") +"Selecting Race: " + ToString(nRace),oChar);

    if (!bUndo && nRace != GetCreatureRacialType(oChar))
    {
        // -------------------------------------------------------------------------
        // 1. SetAppearance
        //    - Retrieve the appearance for the race from races.xls
        //    - Set Appearance
        //    - Set Racial Type
        // -------------------------------------------------------------------------

        if (nRace == RACE_HUMAN || nRace == RACE_DWARF || nRace == RACE_ELF )
        {
            int nApp = GetM2DAInt(TABLE_RULES_RACES, "Appearance", nRace);
            if (GetCreatureRacialType(oChar) != nRace)
            {
                SetAppearanceType(oChar,nApp, TRUE);
                SetCreatureRacialType(oChar,nRace);
            }


        }
    }

 // Humanoids gain the 'humanoid' trait (prereq for skills)
    if (bUndo)
    {
        RemoveAbility(oChar,ABILITY_TALENT_TRAIT_HUMANOID);
    }
    else if (IsHumanoid(oChar))
    {
        AddAbility(oChar,ABILITY_TALENT_TRAIT_HUMANOID,FALSE);
    }


    // -------------------------------------------------------------------------
    // Dwaves gain dwarven resistance free.
    // -------------------------------------------------------------------------
    if (nRace == RACE_DWARF)
    {
        if (!bUndo)
        {
            AddAbility(oChar, ABILITY_SKILL_DWARVEN_RESISTANCE, FALSE);
        }
        else
        {
            RemoveAbility(oChar, ABILITY_SKILL_DWARVEN_RESISTANCE);
        }
    }

    Chargen_ApplyRaceModifiers(oChar, bUndo);
}




void Chargen_SelectBackground(object oChar, int nBackground, int bUnApply = FALSE)
{

     Log_Chargen("Chargen_SelectBackground","-- " + (bUnApply?"Un":"") +"Selecting BG: " + ToString(nBackground),oChar);

    // -------------------------------------------------------------------------
    // 1. Set the background variable
    //          - Create creature property (or check what we used so far
    //          - We don't set backgrounds on non player generated chars..
    // -------------------------------------------------------------------------

    if (bUnApply)
    {
        SetCreatureProperty(oChar, PROPERTY_SIMPLE_BACKGROUND, 0.0, PROPERTY_VALUE_BASE);
    }
    else
    {
       SetCreatureProperty(oChar, PROPERTY_SIMPLE_BACKGROUND, IntToFloat(nBackground), PROPERTY_VALUE_BASE);
    }


    // -------------------------------------------------------------------------
    // 2. Give one skill
    //    - retrieve the skill that is granted by the background from backgrounds.xls
    //    - give it to the player.
    // -------------------------------------------------------------------------


    int nAbility = 0;
    int nAbility1 = 0;

    switch (nBackground)
    {
        case BACKGROUND_CITY:           nAbility = ABILITY_SKILL_PERSUADE_1;    break;
        case BACKGROUND_COMMONER:       nAbility = ABILITY_SKILL_STEALING_1;    break;
        case BACKGROUND_DWARF_COMMONER: nAbility = ABILITY_SKILL_STEALING_1;    break;
        case BACKGROUND_DALISH:         nAbility = ABILITY_SKILL_SURVIVAL_1;    break;
        case BACKGROUND_MAGI:           nAbility = ABILITY_SKILL_COMBAT_TACTICS_1;    break;
        case BACKGROUND_NOBLE:          nAbility = (!HasAbility(oChar,ABILITY_SKILL_COMBAT_TRAINING_1))?ABILITY_SKILL_COMBAT_TRAINING_1:ABILITY_SKILL_COMBAT_TRAINING_2; break;
        case BACKGROUND_DWARF_NOBLE:    nAbility = (!HasAbility(oChar,ABILITY_SKILL_COMBAT_TRAINING_1))?ABILITY_SKILL_COMBAT_TRAINING_1:ABILITY_SKILL_COMBAT_TRAINING_2; break;
    }




    if (nAbility)
    {
        _AddAbility (oChar, nAbility, bUnApply);
    }

}


/*
 *  @brief Sets the character's core clas
 *
 *  Sets the character's core class, including attributes, abilities and stats
 *
 *  @param oChar The character
 *  @param nClas The id of the core class (CLASS_*)
 *
 *  @author Georg Zoeller
*/
void Chargen_SelectCoreClass(object oChar,int nClass, int bUnApply = FALSE);
void Chargen_SelectCoreClass(object oChar,int nClass, int bUnApply = FALSE)
{

      Log_Chargen("Chargen_SelectCoreClass","-- " + (bUnApply?"Un":"") +"Selecting Class: " + ToString(nClass),oChar);



      // -------------------------------------------------------------------------
      // 1. Set current class
      //        - Check engine implications
      //        - Get function to reset class array
      // -------------------------------------------------------------------------
      if (bUnApply)
      {
          SetCreatureProperty(oChar,PROPERTY_SIMPLE_CURRENT_CLASS, 0.0, PROPERTY_VALUE_BASE);
      }
      else
      {
          SetCreatureProperty(oChar,PROPERTY_SIMPLE_CURRENT_CLASS, IntToFloat(nClass), PROPERTY_VALUE_BASE);
      }


      // -------------------------------------------------------------------------
      // 2. Apply Class Specific Attribute Modifiers
      // -------------------------------------------------------------------------
      Chargen_ApplyClassAttributeModifiers(oChar, nClass,bUnApply);


      // -------------------------------------------------------------------------
      // 3. Set the initial character stats based on Class (Health, Mana, etc..)
      // -------------------------------------------------------------------------
      Chargen_ApplyClassStatModifiers(oChar, nClass,bUnApply);


      // -------------------------------------------------------------------------
      // 4. Grant class abilities  (hidden class talent)
      // -------------------------------------------------------------------------
      Chargen_ApplyClassAbilities(oChar, nClass,bUnApply);





}

/*
 *  @brief
 *
 *  <Description>
 *
 *  @param oChar The character
 *
 *  @author Georg Zoeller
*/
int  Chargen_SpendAttributePoints(object oChar,int nAttribute, int nPoints, int  bLevelup = TRUE);
int  Chargen_SpendAttributePoints(object oChar,int nAttribute, int nPoints,  int  bLevelup = TRUE)
{

    Log_Chargen("Chargen_SpendAttributePoints","-- Spending " + ToString(nPoints) + " on " + ToString(nAttribute),oChar);

    // -------------------------------------------------------------------------
    // 1. Distribute Attribute Points
    // -------------------------------------------------------------------------
    Chargen_ModifyCreaturePropertyBase(oChar, nAttribute, IntToFloat(nPoints));

    return TRUE;

}



void Chargen_SetupPlotFlags(object oChar)
{
    int nRace       =  GetCreatureRacialType(oChar);
    int nBackground = FloatToInt(GetCreatureProperty(oChar, PROPERTY_SIMPLE_BACKGROUND,PROPERTY_VALUE_BASE));

    Log_Trace(LOG_CHANNEL_CHARACTER,"sys_chargen_h","Setting plot flags, race: " + IntToString(nRace) + ", background: " + IntToString(nBackground));

    // First, init all flags (debug setup)
    WR_SetPlotFlag(PLT_GEN00PT_BACKGROUNDS, GEN_BACK_HUMAN_COMMONER,FALSE);
    WR_SetPlotFlag(PLT_GEN00PT_BACKGROUNDS, GEN_BACK_HUMAN_NOBLE,FALSE);
    WR_SetPlotFlag(PLT_GEN00PT_BACKGROUNDS, GEN_BACK_CIRCLE,FALSE);
    WR_SetPlotFlag(PLT_GEN00PT_BACKGROUNDS, GEN_BACK_DWARF_COMMONER,FALSE);
    WR_SetPlotFlag(PLT_GEN00PT_BACKGROUNDS, GEN_BACK_DWARF_NOBLE,FALSE);
    WR_SetPlotFlag(PLT_GEN00PT_BACKGROUNDS, GEN_BACK_ELF_CITY,FALSE);
    WR_SetPlotFlag(PLT_GEN00PT_BACKGROUNDS, GEN_BACK_ELF_DALISH,FALSE);
    WR_SetPlotFlag(PLT_GEN00PT_BACKGROUNDS, GEN_BACK_CIRCLE,FALSE);
    switch (nRace)
    {
        case RACE_HUMAN:
        {
            switch(nBackground)
            {
                case BACKGROUND_COMMONER: WR_SetPlotFlag(PLT_GEN00PT_BACKGROUNDS, GEN_BACK_HUMAN_COMMONER,TRUE); break;
                case BACKGROUND_NOBLE: WR_SetPlotFlag(PLT_GEN00PT_BACKGROUNDS, GEN_BACK_HUMAN_NOBLE,TRUE); break;
                case BACKGROUND_MAGI: WR_SetPlotFlag(PLT_GEN00PT_BACKGROUNDS, GEN_BACK_CIRCLE,TRUE); break;
            }
            break;
        }
        case RACE_DWARF:
        {
            switch(nBackground)
            {
                case BACKGROUND_COMMONER: WR_SetPlotFlag(PLT_GEN00PT_BACKGROUNDS, GEN_BACK_DWARF_COMMONER,TRUE); break;
                case BACKGROUND_NOBLE: WR_SetPlotFlag(PLT_GEN00PT_BACKGROUNDS, GEN_BACK_DWARF_NOBLE,TRUE); break;
            }
            break;
        }
        case RACE_ELF:
        {
            switch(nBackground)
            {
                case BACKGROUND_CITY: WR_SetPlotFlag(PLT_GEN00PT_BACKGROUNDS, GEN_BACK_ELF_CITY,TRUE); break;
                case BACKGROUND_DALISH: WR_SetPlotFlag(PLT_GEN00PT_BACKGROUNDS, GEN_BACK_ELF_DALISH,TRUE); break;
                case BACKGROUND_MAGI: WR_SetPlotFlag(PLT_GEN00PT_BACKGROUNDS, GEN_BACK_CIRCLE,TRUE); break;
            }
            break;
        }
    }
}


// enabling proper tactics presets based on class (might change at levelup)
void Chargen_EnableTacticsPresets(object oCreature);
void Chargen_EnableTacticsPresets(object oCreature)
{
    int nCoreClass = GetCreatureCoreClass(oCreature);
    int nTotalPresetsNum = GetM2DARows(TABLE_TACTICS_USER_PRESETS);
    int j;
    int nForClass1, nForClass2, nForClass3;
    int nPresetTable;
    int nCurrentRow;
    for(j = 0; j < nTotalPresetsNum; j++)
    {
        nCurrentRow = GetM2DARowIdFromRowIndex(TABLE_TACTICS_USER_PRESETS, j);
        nPresetTable = GetM2DAInt(TABLE_TACTICS_USER_PRESETS, "TacticsTable", nCurrentRow);
        if(nPresetTable > 0)
        {
            nForClass1 = GetM2DAInt(TABLE_TACTICS_USER_PRESETS, "ValidForClass1", nCurrentRow);
            nForClass2 = GetM2DAInt(TABLE_TACTICS_USER_PRESETS, "ValidForClass2", nCurrentRow);
            nForClass3 = GetM2DAInt(TABLE_TACTICS_USER_PRESETS, "ValidForClass3", nCurrentRow);
            if(nCoreClass == nForClass1 || nCoreClass == nForClass2 || nCoreClass == nForClass3)
                AddTacticPresetID(oCreature, nCurrentRow);
        }
    }

}

void Chargen_LoadPresetsTable(object oCreature, int nPresetID);
void Chargen_LoadPresetsTable(object oCreature, int nPresetID)
{
    int nTableID = GetM2DAInt(TABLE_TACTICS_USER_PRESETS, "TacticsTable", nPresetID);
    if (nTableID != 0)
    {
        int nRows = GetM2DARows(nTableID);
        int nMaxTactics = GetNumTactics(oCreature);

        int nTacticsEntry = 1;
        int i;
        int nCurrentRow;
        for (i = 0; i < nRows && nTacticsEntry <= nMaxTactics; ++i)
        {
            int bAddEntry = FALSE;
            nCurrentRow = GetM2DARowIdFromRowIndex(nTableID, i);
            Log_Trace(LOG_CHANNEL_CHARACTER,"Chargen_LoadPresetsTable","setting presets, index: " + IntToString(i) + ", row: " + IntToString(nCurrentRow));
            int nTargetType = GetM2DAInt(nTableID, "TargetType", nCurrentRow);
            int nCondition = GetM2DAInt(nTableID, "Condition", nCurrentRow);
            int nCommandType = GetM2DAInt(nTableID, "Command", nCurrentRow);
            int nCommandParam = GetM2DAInt(nTableID, "SubCommand", nCurrentRow);

            int nUseType = GetM2DAInt(TABLE_COMMAND_TYPES, "UseType", nCommandType);
            if (nUseType == 0)
            {
                bAddEntry = TRUE;
            }
            else
            {
                bAddEntry = HasAbility(oCreature, nCommandParam);
            }

            if (bAddEntry)
            {
                SetTacticEntry(oCreature, nTacticsEntry, TRUE, nTargetType, nCondition, nCommandType, nCommandParam);
                ++nTacticsEntry;
            }
        }
    }
}

// Will re-populate the creature's tactic table if the creature still has a pre-defined preset
// it will do nothing if the creature changed the perset or has nothing selected
void Chargen_PopulateTacticsForPreset(object oCreature);
void Chargen_PopulateTacticsForPreset(object oCreature)
{
    int nCurrentPreset = GetTacticPresetID(oCreature);
    if(nCurrentPreset > 0) // ==> still has a valid preset - re-populate list
    {
        Chargen_LoadPresetsTable(oCreature, nCurrentPreset);
    }

}